const { ccclass, property } = cc._decorator;


@ccclass
export default class ScrollSelect extends cc.Component {
    @property(cc.Node)
    content: cc.Node = null;
    @property({
        type: cc.Integer,
        tooltip: '单个控件之间的距离(x间隔距离)'
    })
    deltaX: number = 100;
    @property({
        type: cc.Float,
        tooltip: '中心点的缩放比例'
    })
    centerScale: number = 1.2;
    @property({
        type: cc.Float,
        tooltip: '边缘点的缩放比例'
    })
    minScale: number = 1.0
    @property({
        type: cc.Integer,
        tooltip: '中心点的透明度'
    })
    centerOpacity: number = 255;
    @property({
        type: cc.Integer,
        tooltip: '边缘点的透明度'
    })
    minOpacity: number = 100
    @property({
        type: cc.Integer,
        tooltip: '中心的两边预加载个数'
    })
    scrollNumber: number = 1;
    @property({
        type: cc.Integer,
        tooltip: '默认选中的节点序号'
    })
    checkedId: number = -1;
    @property({
        type: cc.Integer,
        tooltip: '滚动时的速度'
    })
    scrollSpeed: number = 300;
    @property({
        type: [cc.Component.EventHandler],
        tooltip: "选择后的回调"
    })
    selectEvents: Array<cc.Component.EventHandler> = [];
    atlas: cc.SpriteFrame[] = [];
    ids: string[] = [];
    private hasTouchMove: boolean = false;
    private isTestX: boolean = false;//是否需要移动
    private currentIndex: number = 0;//当前已达到的位置
    private _toMoveX: number = 1; //移动方向
    private dx: number = 0;//开始触摸时的位置
    private moveAim: number = 0; //移动距离位置
    private ScaleItem: number = 0;
    private OpacityItem: number = 0;
    private currentInterval: number = 20;//当X坐标在[-20到20]区间表示选中当前节点
    private state: number = 0;
    private midIndex: number = 0;
    private worldX: number = 0;
    onLoad() {
        this.hasTouchMove = false
        this.isTestX = false;
        this.currentInterval = this.deltaX / 2;
        this.worldX = this.node.convertToWorldSpaceAR(cc.v2(0, 0)).x;
        this.loadChildren();
    }
    start() {
        this.node.on(cc.Node.EventType.TOUCH_START, this._onTouch, this);
        this.node.on(cc.Node.EventType.TOUCH_MOVE, this._onTouch, this);
        this.node.on(cc.Node.EventType.TOUCH_END, this._onTouchEnd, this);
        this.node.on(cc.Node.EventType.TOUCH_CANCEL, this._onTouchEnd, this);
        this.scrollTo(this.checkedId, false);
    }
    loadChildren() {
        let mid = Math.floor(this.atlas.length / 2);
        if (this.checkedId == -1)
            this.checkedId = mid;

        for (let i = 0; i < this.atlas.length; i++) {
            let node = new cc.Node();
            node.name = this.ids[i];
            node.x = this.deltaX * (i - mid)
            node.active = false;
            node.scale = this.minScale;
            let sprite = node.addComponent(cc.Sprite);
            sprite.spriteFrame = this.atlas[i];
            this.content.addChild(node);
        }
    }
    reLoadChildren() {
        this.content.removeAllChildren();
        this.checkedId = -1;
        this.hasTouchMove = false
        this.isTestX = false;
        this.currentInterval = this.deltaX / 2;
        this.worldX = this.node.convertToWorldSpaceAR(cc.v2(0, 0)).x;
        this.loadChildren();
        this.scrollTo(this.checkedId, false);
    }

    /** 滚动到指定节点 
    * @param anim 是否带移动动画
    */
    scrollTo(idx: number, anim: boolean = true) {
        if (idx < 0 || idx >= this.content.children.length) {
            return;
        }
        if (this.state != 0)
            return;
        if (!anim) {
            this._SetChildX(idx);
            this.emitEvents();
        } else {
            this.isTestX = true
            this.midIndex = Math.floor(this.content.children.length / 2);
            this.moveAim = (idx - this.midIndex) * this.deltaX;
            if (this.currentIndex < idx) {
                this._toMoveX = -1;
            }
            else {
                this._toMoveX = 1;
            }
        }
    }
    private emitEvents() {
        cc.Component.EventHandler.emitEvents(this.selectEvents, {
            target: this,
            index: this.currentIndex,
            id: this.content.children[this.currentIndex].name
        });
    }
    private GetScaleItem() {
        this.ScaleItem = (this.centerScale - this.minScale) / this.scrollNumber;
        this.OpacityItem = (this.centerOpacity - this.minOpacity) / this.scrollNumber;
    }
    private _SetChildX(xIndex: number) {
        let mid = Math.floor(this.content.children.length / 2);
        let midNode = this.content.children[xIndex];
        midNode.scale = this.centerScale;
        midNode.opacity = this.centerOpacity;
        midNode.active = true;
        this.content.x = this.deltaX * (mid - xIndex);
        this.GetScaleItem();
        for (let i = 1; i <= this.scrollNumber; i++) {
            if (xIndex - i >= 0) {
                let leftNode = this.content.children[xIndex - i];
                leftNode.scale = this.centerScale - i * this.ScaleItem;
                leftNode.opacity = this.centerOpacity - i * this.OpacityItem;
                leftNode.active = true;
            }
            if (xIndex + i < this.content.children.length) {
                let rightNode = this.content.children[xIndex + i];
                rightNode.scale = this.centerScale - i * this.ScaleItem;
                rightNode.opacity = this.centerOpacity - i * this.OpacityItem;
                rightNode.active = true;
            }
        }
        let leftB = xIndex - this.scrollNumber - 1;
        if (leftB >= 0) {
            let leftNode = this.content.children[leftB];
            leftNode.active = false;
        }
        let rightB = xIndex + this.scrollNumber + 1;
        if (rightB < this.content.children.length) {
            let leftNode = this.content.children[rightB];
            leftNode.active = false;
        }
        this.currentIndex = xIndex;
    }
    private _onTouch(event) {
        if (event.type == cc.Node.EventType.TOUCH_START) {
            this.midIndex = Math.floor(this.content.children.length / 2);
            this.dx = event.getStartLocation().x;
        }
        else {
            let _dx = event.getLocation().x - this.dx
            if (_dx > 5 || _dx < -5) {//灵敏度
                this.hasTouchMove = true;
                if (this._checkTouchMove(_dx))
                    this.dx = event.getLocation().x;
            }
        }
    }
    private _onTouchEnd(event) {
        if (!this.hasTouchMove && !this.isTestX) {
            if (this.dx < this.worldX - this.currentInterval) {
                this.scrollTo(this.currentIndex - 1, true);
            }
            else if (this.dx > this.worldX + this.currentInterval) {
                this.scrollTo(this.currentIndex + 1, true);
            }
        }
        this.hasTouchMove = false;
    }
    private _checkTouchMove(_dx: number): boolean {
        let xx = Math.floor(_dx / this.currentInterval);
        if (xx != 0) {
            this.scrollTo(this.currentIndex + xx * -1, true);
            return true;
        }
        return false;
    }
    private MoveNode(dt) {
        let moveX = this.scrollSpeed * dt;
        this.content.x += moveX * this._toMoveX;
        if (this._toMoveX > 0) {
            if (this.moveAim + this.content.x > 0) {
                this.content.x = 0 - this.moveAim;
                this.isTestX = false;
            }
        }
        else {
            if (this.moveAim + this.content.x < 0) {
                this.content.x = 0 - this.moveAim;
                this.isTestX = false;
            }
        }
        let opacity = this.OpacityItem * moveX / this.deltaX;
        let scale = this.ScaleItem * moveX / this.deltaX;
        for (let i = 1; i <= this.scrollNumber; i++) {
            if (this.currentIndex - i >= 0) {
                let leftNode = this.content.children[this.currentIndex - i];
                this.SetMoveItem(leftNode, opacity * this._toMoveX, scale * this._toMoveX);
            }
            if (this.currentIndex + i < this.content.children.length) {
                let rightNode = this.content.children[this.currentIndex + i];
                this.SetMoveItem(rightNode, (0 - opacity) * this._toMoveX, (0 - scale) * this._toMoveX);
            }
        }
        this.moveCenterNode();
    }
    private moveCenterNode() {
        if (this.state == 0) {
            let midX = this.content.x - this.deltaX * (this.midIndex - this.currentIndex);
            if (midX > this.currentInterval) {
                if (this.currentIndex + this.scrollNumber < this.content.children.length)
                    this.content.children[this.currentIndex + this.scrollNumber].active = false;
                if (this.currentIndex == 0) {
                    this.isTestX = false;
                }
                else {
                    this.currentIndex--;
                    let nextMidNode = this.content.children[this.currentIndex];
                    this.newCenter(nextMidNode);
                    this.state = 1;
                }
            }
            else if (midX < 0 - this.currentInterval) {
                if (this.currentIndex - this.scrollNumber >= 0)
                    this.content.children[this.currentIndex - this.scrollNumber].active = false;
                if (this.currentIndex == this.content.children.length - 1) {
                    this.isTestX = false;
                }
                else {
                    this.currentIndex++;
                    let nextMidNode = this.content.children[this.currentIndex];
                    this.newCenter(nextMidNode);
                    this.state = -1;
                }
            }
        }
        else {
            let cc = (this.currentIndex - this.midIndex) * this.deltaX;
            if (this.state > 0) {
                if (this.content.x + cc >= 0) {
                    this.state = 0;
                }
            }
            else {
                if (this.content.x + cc <= 0) {
                    this.state = 0;
                }
            }

        }
    }
    private newCenter(node: cc.Node) {
        node.opacity = this.centerOpacity;
        node.scale = this.centerScale;
        node.active = true;
        this.emitEvents();
    }
    private SetMoveItem(node: cc.Node, opacity: number, scale: number) {
        node.scale += scale;
        node.opacity += opacity;
        node.active = true;
        if (node.opacity < this.minOpacity) {
            node.scale = this.minScale;
            node.opacity = this.minOpacity;
        }
    }
    leftNext() {
        this.scrollTo(this.currentIndex - 1, true);
    }
    rightNext() {
        this.scrollTo(this.currentIndex + 1, true);
    }
    update(dt) {
        if (this.isTestX) {
            this.MoveNode(dt);
        }
    }
}
